// models/SysRoleMenu.js
const mongoose = require("mongoose");
const { Schema } = mongoose;

/**
 * 角色和菜单关联表 - 对应 MySQL sys_role_menu 表
 * 多对多关系，使用复合主键
 */
const sysRoleMenuSchema = new Schema(
  {
    // 角色ID - 对应MySQL的role_id
    roleId: {
      type: Number,
      required: [true, "角色ID不能为空"],
      index: true,
      comment: "角色ID",
    },

    // 菜单ID - 对应MySQL的menu_id
    menuId: {
      type: Number,
      required: [true, "菜单ID不能为空"],
      index: true,
      comment: "菜单ID",
    },

    // 创建时间 - MongoDB扩展字段，便于查询和管理
    createTime: {
      type: Date,
      default: Date.now,
      comment: "创建时间",
    },

    // 创建者 - MongoDB扩展字段
    createBy: {
      type: String,
      default: "",
      comment: "创建者",
    },
  },
  {
    // Schema 选项
    timestamps: false,
    collection: "sys_role_menus",
  }
);

// 创建复合唯一索引，模拟MySQL的复合主键
sysRoleMenuSchema.index({ roleId: 1, menuId: 1 }, { unique: true });

// 创建反向索引，便于双向查询
sysRoleMenuSchema.index({ menuId: 1, roleId: 1 });

// 虚拟字段 - 角色信息（需要populate）
sysRoleMenuSchema.virtual("role", {
  ref: "SysRole",
  localField: "roleId",
  foreignField: "roleId",
  justOne: true,
});

// 虚拟字段 - 菜单信息（需要populate）
sysRoleMenuSchema.virtual("menu", {
  ref: "SysMenu",
  localField: "menuId",
  foreignField: "menuId",
  justOne: true,
});

// 实例方法
sysRoleMenuSchema.methods = {
  // 获取关联信息（包含角色和菜单详情）
  toDetailJSON: async function () {
    await this.populate("role", "roleName roleKey status");
    await this.populate("menu", "menuName menuType path perms status visible");

    return {
      roleId: this.roleId,
      menuId: this.menuId,
      role: this.role
        ? {
            roleName: this.role.roleName,
            roleKey: this.role.roleKey,
            status: this.role.status,
          }
        : null,
      menu: this.menu
        ? {
            menuName: this.menu.menuName,
            menuType: this.menu.menuType,
            path: this.menu.path,
            perms: this.menu.perms,
            status: this.menu.status,
            visible: this.menu.visible,
          }
        : null,
      createTime: this.createTime,
      createBy: this.createBy,
    };
  },
};

// 静态方法
sysRoleMenuSchema.statics = {
  /**
   * 为角色分配菜单权限
   */
  assignMenusToRole: function (roleId, menuIds, createBy = "") {
    // 先删除角色的所有现有菜单权限
    return this.deleteRoleMenus(roleId).then(() => {
      // 批量插入新的菜单权限关联
      const roleMenus = menuIds.map((menuId) => ({
        roleId: roleId,
        menuId: menuId,
        createBy: createBy,
        createTime: new Date(),
      }));

      return this.insertMany(roleMenus);
    });
  },

  /**
   * 为菜单分配角色
   */
  assignRolesToMenu: function (menuId, roleIds, createBy = "") {
    // 先删除菜单的所有现有角色关联
    return this.deleteMenuRoles(menuId).then(() => {
      // 批量插入新的角色关联
      const roleMenus = roleIds.map((roleId) => ({
        roleId: roleId,
        menuId: menuId,
        createBy: createBy,
        createTime: new Date(),
      }));

      return this.insertMany(roleMenus);
    });
  },

  /**
   * 删除角色的所有菜单权限
   */
  deleteRoleMenus: function (roleId) {
    return this.deleteMany({ roleId: roleId });
  },

  /**
   * 删除菜单的所有角色关联
   */
  deleteMenuRoles: function (menuId) {
    return this.deleteMany({ menuId: menuId });
  },

  /**
   * 删除特定的角色菜单关联
   */
  deleteRoleMenu: function (roleId, menuId) {
    return this.deleteOne({ roleId: roleId, menuId: menuId });
  },

  /**
   * 检查角色是否拥有某个菜单权限
   */
  hasRoleMenu: function (roleId, menuId) {
    return this.findOne({ roleId: roleId, menuId: menuId });
  },

  /**
   * 检查角色是否拥有某个菜单权限（通过权限标识）
   */
  hasRoleMenuByPerms: async function (roleId, perms) {
    const SysMenu = mongoose.model("SysMenu");
    const menu = await SysMenu.findOne({ perms: perms });

    if (!menu) {
      return false;
    }

    return this.findOne({ roleId: roleId, menuId: menu.menuId });
  },

  /**
   * 获取角色的所有菜单ID
   */
  getRoleMenuIds: function (roleId) {
    return this.find({ roleId: roleId })
      .select("menuId -_id")
      .then((menus) => menus.map((menu) => menu.menuId));
  },

  /**
   * 获取角色的所有菜单信息
   */
  getRoleMenus: function (roleId, options = {}) {
    const {
      includeHidden = false,
      includeDisabled = false,
      menuTypes = ["M", "C", "F"],
    } = options;

    let query = { roleId: roleId };

    return this.find(query)
      .populate({
        path: "menu",
        match: {
          ...(!includeDisabled && { status: "0" }),
          ...(!includeHidden && { visible: "0" }),
          ...(menuTypes && { menuType: { $in: menuTypes } }),
        },
        select:
          "menuId menuName parentId orderNum path component isFrame isCache menuType visible status perms icon",
      })
      .then((roleMenus) =>
        roleMenus
          .filter((roleMenu) => roleMenu.menu !== null) // 过滤掉被populate条件排除的菜单
          .map((roleMenu) => roleMenu.menu)
      );
  },

  /**
   * 获取角色的菜单树
   */
  getRoleMenuTree: async function (roleId, options = {}) {
    const SysMenu = mongoose.model("SysMenu");
    const menuIds = await this.getRoleMenuIds(roleId);

    const {
      includeHidden = false,
      includeDisabled = false,
      menuTypes = ["M", "C"], // 默认只返回目录和菜单，不包含按钮
    } = options;

    const query = {
      menuId: { $in: menuIds },
    };

    if (!includeDisabled) {
      query.status = "0";
    }

    if (!includeHidden) {
      query.visible = "0";
    }

    if (menuTypes && menuTypes.length > 0) {
      query.menuType = { $in: menuTypes };
    }

    const menus = await SysMenu.find(query).sort({ parentId: 1, orderNum: 1 });

    return SysMenu.buildMenuTree(menus);
  },

  /**
   * 获取菜单的所有角色ID
   */
  getMenuRoleIds: function (menuId) {
    return this.find({ menuId: menuId })
      .select("roleId -_id")
      .then((roles) => roles.map((role) => role.roleId));
  },

  /**
   * 获取菜单的所有角色信息
   */
  getMenuRoles: function (menuId) {
    return this.find({ menuId: menuId })
      .populate("role", "roleId roleName roleKey status")
      .then((roles) => roles.map((role) => role.role));
  },

  /**
   * 批量获取角色的菜单信息
   */
  getRolesMenus: function (roleIds) {
    return this.find({ roleId: { $in: roleIds } })
      .populate("menu", "menuId menuName menuType path perms status visible")
      .then((relations) => {
        const result = {};
        relations.forEach((relation) => {
          if (!result[relation.roleId]) {
            result[relation.roleId] = [];
          }
          result[relation.roleId].push(relation.menu);
        });
        return result;
      });
  },

  /**
   * 批量获取菜单的角色信息
   */
  getMenusRoles: function (menuIds) {
    return this.find({ menuId: { $in: menuIds } })
      .populate("role", "roleId roleName roleKey status")
      .then((relations) => {
        const result = {};
        relations.forEach((relation) => {
          if (!result[relation.menuId]) {
            result[relation.menuId] = [];
          }
          result[relation.menuId].push(relation.role);
        });
        return result;
      });
  },

  /**
   * 分页查询角色菜单关联
   */
  findRoleMenusPaginated: function (options = {}) {
    const {
      page = 1,
      limit = 10,
      roleId = null,
      menuId = null,
      menuType = null,
    } = options;

    const skip = (page - 1) * limit;
    const query = {};

    if (roleId) {
      query.roleId = roleId;
    }

    if (menuId) {
      query.menuId = menuId;
    }

    let populateQuery = [
      { path: "role", select: "roleName roleKey status" },
      { path: "menu", select: "menuName menuType path perms status visible" },
    ];

    // 如果指定了菜单类型，添加过滤条件
    if (menuType) {
      populateQuery[1].match = { menuType: menuType };
    }

    return Promise.all([
      this.find(query)
        .populate(populateQuery)
        .skip(skip)
        .limit(limit)
        .sort({ createTime: -1 }),
      this.countDocuments(query),
    ]).then(([roleMenus, total]) => ({
      roleMenus: roleMenus.filter((rm) => rm.role && rm.menu), // 过滤掉无效的关联
      pagination: {
        page: parseInt(page),
        limit: parseInt(limit),
        total,
        pages: Math.ceil(total / limit),
      },
    }));
  },

  /**
   * 统计角色菜单数量
   */
  getRoleMenuStats: function () {
    return this.aggregate([
      {
        $group: {
          _id: "$roleId",
          menuCount: { $sum: 1 },
          uniqueMenuTypes: { $addToSet: "$menuType" },
        },
      },
      {
        $lookup: {
          from: "sys_roles",
          localField: "_id",
          foreignField: "roleId",
          as: "roleInfo",
        },
      },
      {
        $unwind: "$roleInfo",
      },
      {
        $group: {
          _id: null,
          totalRoles: { $sum: 1 },
          avgMenusPerRole: { $avg: "$menuCount" },
          maxMenusPerRole: { $max: "$menuCount" },
          minMenusPerRole: { $min: "$menuCount" },
        },
      },
      {
        $project: {
          _id: 0,
          totalRoles: 1,
          avgMenusPerRole: { $round: ["$avgMenusPerRole", 2] },
          maxMenusPerRole: 1,
          minMenusPerRole: 1,
        },
      },
    ]);
  },

  /**
   * 统计菜单角色数量
   */
  getMenuRoleStats: function () {
    return this.aggregate([
      {
        $group: {
          _id: "$menuId",
          roleCount: { $sum: 1 },
        },
      },
      {
        $lookup: {
          from: "sys_menus",
          localField: "_id",
          foreignField: "menuId",
          as: "menuInfo",
        },
      },
      {
        $unwind: "$menuInfo",
      },
      {
        $group: {
          _id: null,
          totalMenus: { $sum: 1 },
          avgRolesPerMenu: { $avg: "$roleCount" },
          maxRolesPerMenu: { $max: "$roleCount" },
          minRolesPerMenu: { $min: "$roleCount" },
        },
      },
      {
        $project: {
          _id: 0,
          totalMenus: 1,
          avgRolesPerMenu: { $round: ["$avgRolesPerMenu", 2] },
          maxRolesPerMenu: 1,
          minRolesPerMenu: 1,
        },
      },
    ]);
  },

  /**
   * 获取菜单数量最多的角色
   */
  getRolesWithMostMenus: function (limit = 10) {
    return this.aggregate([
      {
        $group: {
          _id: "$roleId",
          menuCount: { $sum: 1 },
        },
      },
      {
        $sort: { menuCount: -1 },
      },
      {
        $limit: limit,
      },
      {
        $lookup: {
          from: "sys_roles",
          localField: "_id",
          foreignField: "roleId",
          as: "roleInfo",
        },
      },
      {
        $unwind: "$roleInfo",
      },
      {
        $project: {
          roleId: "$_id",
          roleName: "$roleInfo.roleName",
          roleKey: "$roleInfo.roleKey",
          menuCount: 1,
          _id: 0,
        },
      },
    ]);
  },

  /**
   * 获取角色数量最多的菜单
   */
  getMenusWithMostRoles: function (limit = 10) {
    return this.aggregate([
      {
        $group: {
          _id: "$menuId",
          roleCount: { $sum: 1 },
        },
      },
      {
        $sort: { roleCount: -1 },
      },
      {
        $limit: limit,
      },
      {
        $lookup: {
          from: "sys_menus",
          localField: "_id",
          foreignField: "menuId",
          as: "menuInfo",
        },
      },
      {
        $unwind: "$menuInfo",
      },
      {
        $project: {
          menuId: "$_id",
          menuName: "$menuInfo.menuName",
          menuType: "$menuInfo.menuType",
          path: "$menuInfo.path",
          roleCount: 1,
          _id: 0,
        },
      },
    ]);
  },

  /**
   * 根据用户ID获取菜单权限
   * 通过用户角色关联查询用户有权限的菜单
   */
  getUserMenus: async function (userId, options = {}) {
    const SysUserRole = mongoose.model("SysUserRole");

    // 获取用户的所有角色ID
    const roleIds = await SysUserRole.getUserRoleIds(userId);

    if (roleIds.length === 0) {
      return [];
    }

    // 获取这些角色的所有菜单ID（去重）
    const menuIds = await this.find({ roleId: { $in: roleIds } }).distinct(
      "menuId"
    );

    if (menuIds.length === 0) {
      return [];
    }

    // 获取菜单详情
    const SysMenu = mongoose.model("SysMenu");
    const {
      includeHidden = false,
      includeDisabled = false,
      menuTypes = ["M", "C"],
    } = options;

    const query = {
      menuId: { $in: menuIds },
    };

    if (!includeDisabled) {
      query.status = "0";
    }

    if (!includeHidden) {
      query.visible = "0";
    }

    if (menuTypes && menuTypes.length > 0) {
      query.menuType = { $in: menuTypes };
    }

    return SysMenu.find(query).sort({ parentId: 1, orderNum: 1 });
  },

  /**
   * 根据用户ID获取菜单树
   */
  getUserMenuTree: async function (userId, options = {}) {
    const SysMenu = mongoose.model("SysMenu");
    const menus = await this.getUserMenus(userId, options);
    return SysMenu.buildMenuTree(menus);
  },

  /**
   * 根据用户ID获取权限字符串列表
   */
  getUserMenuPerms: async function (userId) {
    const menus = await this.getUserMenus(userId, {
      menuTypes: ["F"], // 只获取按钮类型的权限
    });

    return menus
      .filter((menu) => menu.perms && menu.perms.trim() !== "")
      .map((menu) => menu.perms);
  },

  /**
   * 检查用户是否有某个菜单权限
   */
  hasUserMenuPerm: async function (userId, perms) {
    const userPerms = await this.getUserMenuPerms(userId);
    return userPerms.includes(perms);
  },
};

// 查询助手
sysRoleMenuSchema.query = {
  // 按角色ID筛选
  byRoleId: function (roleId) {
    return this.where("roleId").equals(roleId);
  },

  // 按菜单ID筛选
  byMenuId: function (menuId) {
    return this.where("menuId").equals(menuId);
  },

  // 按菜单类型筛选（需要populate后过滤）
  byMenuType: function (menuType) {
    return this.where("menu.menuType").equals(menuType);
  },

  // 按创建时间范围筛选
  byCreateTimeRange: function (startDate, endDate) {
    return this.where("createTime").gte(startDate).lte(endDate);
  },
};

// 中间件
sysRoleMenuSchema.pre("save", async function (next) {
  // 验证角色和菜单是否存在且状态正常
  try {
    const SysRole = mongoose.model("SysRole");
    const SysMenu = mongoose.model("SysMenu");

    const [role, menu] = await Promise.all([
      SysRole.findByRoleId(this.roleId),
      SysMenu.findByMenuId(this.menuId),
    ]);

    if (!role) {
      return next(new Error(`角色ID ${this.roleId} 不存在`));
    }

    if (!menu) {
      return next(new Error(`菜单ID ${this.menuId} 不存在`));
    }

    if (role.status !== "0") {
      return next(new Error(`角色 ${role.roleName} 已被禁用`));
    }

    if (menu.status !== "0") {
      return next(new Error(`菜单 ${menu.menuName} 已被禁用`));
    }

    next();
  } catch (error) {
    next(error);
  }
});

// 创建模型
const SysRoleMenu = mongoose.model("SysRoleMenu", sysRoleMenuSchema);

module.exports = SysRoleMenu;
